﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Diagnostics;
using System.Threading;

namespace XiaLastorm.Sharpshooter.Client
{
	/// <summary>
	/// 客户端
	/// </summary>
	public class Client : IDisposable
	{
		/// <summary>
		/// 整个应用程序的唯一客户端实例
		/// </summary>
		public static Client AppClient
		{
			get { return (App.Current as App).Client; }
			set
			{
				(App.Current as App).Client = value;
			}
		}
		/// <summary>
		/// 服务器地址
		/// </summary>
		public readonly string ServerHost;
		/// <summary>
		/// 服务器端口
		/// </summary>
		public const int ServerPort = 4660;

		/// <summary>
		/// 获取或设置当前用户
		/// </summary>
		public User User { get; protected set; }

		/// <summary>
		/// 获取或设置客户端与服务器通信时使用的套接字
		/// </summary>
		protected Socket Socket { get; set; }

		/// <summary>
		/// 获取或设置当前用户的好友列表
		/// </summary>
		public List<User> Friends { get; set; }
		/// <summary>
		/// 用户状态枚举
		/// </summary>
		public enum UserStateKind
		{
			/// <summary>
			/// 离线
			/// </summary>
			Offline = 0,
			/// <summary>
			/// 在线
			/// </summary>
			Online
		};
		/// <summary>
		/// 用户状态
		/// </summary>
		public UserStateKind UserState { get; protected set; }

		/// <summary>
		/// 获取一个值，该值指示用户是否在线
		/// </summary>
		/// <value>
		/// 如果在线则为<c>true</c>；否则为<c>false</c>。
		/// </value>
		public bool IsOnline
		{
			get
			{
				return UserState == Client.UserStateKind.Online;
			}
		}

		public Client(string serverHost)
		{
			if (serverHost == null)
				throw new ArgumentNullException("serverHost");
			if (serverHost == string.Empty)
				throw new ArgumentException("服务器地址为空");
			ServerHost = serverHost;
			Log = new List<Log>();
			ChatLog = new List<ChatLog>();
			Friends = new List<User>();
			MessageQueue = new Queue<Message>();
		}
		/// <summary>
		/// 获取或设置客户端日志
		/// </summary>
		public List<Log> Log { get; set; }
		/// <summary>
		/// 添加日志
		/// </summary>
		/// <param name="content">日志内容</param>
		protected void AddLog(string content)
		{
			Log log = new Log(content);
			Log.Add(log);
			RaiseLogAddedEvent(log);
		}
		/// <summary>
		/// 获取或设置聊天记录
		/// </summary>
		public List<ChatLog> ChatLog { get; set; }
		/// <summary>
		/// 添加聊天记录
		/// </summary>
		/// <param name="log">聊天内容</param>
		protected void AddChatLog(ChatLog log)
		{
			ChatLog.Add(log);
			RaiseChatLogAddedEvent(log);
		}
		/// <summary>
		/// 当用户成功登录时发生。
		/// </summary>
		public event EventHandler<UserEventArgs> UserLoggedIn;
		/// <summary>
		/// 引发用户成功登录事件。
		/// </summary>
		/// <param name="user">用户</param>
		protected void RaiseUserLoggedInEvent(User user)
		{
			if (UserLoggedIn != null)
				UserLoggedIn(this, new UserEventArgs(user));
		}
		/// <summary>
		/// 当用户成功注销时发生。
		/// </summary>
		public event EventHandler<UserEventArgs> UserLoggedOut;
		/// <summary>
		/// 引发用户成功注销事件。
		/// </summary>
		/// <param name="user">用户</param>
		protected void RaiseUserLoggedOutEvent(User user)
		{
			if (UserLoggedOut != null)
				UserLoggedOut(this, new UserEventArgs(user));
		}
		/// <summary>
		/// 当好友列表发生改变时发生。
		/// </summary>
		public event EventHandler FriendsListChanged;
		/// <summary>
		/// 引发好友列表发生改变事件。
		/// </summary>
		protected void RaiseFriendsListChangedEvent()
		{
			if (FriendsListChanged != null)
				FriendsListChanged(this, EventArgs.Empty);
		}
		/// <summary>
		/// 当登录失败时发生。
		/// </summary>
		public event EventHandler<ErrorEventArgs> ErrorOccured;
		/// <summary>
		/// 引发登录失败事件。
		/// </summary>
		/// <param name="error">错误信息</param>
		protected void RaiseErrorEvent(string error)
		{
			if (ErrorOccured != null)
				ErrorOccured(this, new ErrorEventArgs(error));
		}
		/// <summary>
		/// 当与服务器丢失连接时发生。
		/// </summary>
		public event EventHandler ConnectionLost;
		/// <summary>
		/// 引发与服务器丢失连接事件。
		/// </summary>
		protected void RaiseConnectionLostEvent()
		{
			if (ConnectionLost != null)
				ConnectionLost(this, EventArgs.Empty);
		}
		/// <summary>
		/// 客户端状态枚举
		/// </summary>
		public enum ClientState { BeforeOpen = 0, Opened, Closed };

		private ClientState state;
		/// <summary>
		/// 状态
		/// </summary>
		public ClientState State
		{
			get { return state; }
			protected set
			{
				if (state != value)
				{
					state = value;
					switch (state)
					{
						case ClientState.BeforeOpen:
							break;
						case ClientState.Opened:
							break;
						case ClientState.Closed:
							break;
						default:
							break;
					}
				}
			}
		}
		
		/// <summary>
		/// 验证客户端还未打开。
		/// </summary>
		protected void ValidateBeforeOpen()
		{
			if (State == ClientState.Opened)
				throw new Exception("客户端处于打开状态");
			if (State == ClientState.Closed)
				throw new Exception("客户端已关闭");
		}

		/// <summary>
		/// 验证客户端已打开。
		/// </summary>
		protected void ValidateOpened()
		{
			if (State == ClientState.BeforeOpen)
				throw new Exception("客户端未打开");
			if (State == ClientState.Closed)
				throw new Exception("客户端已关闭");
		}
		/// <summary>
		/// 验证客户端已关闭。
		/// </summary>
		protected void ValidateClosed()
		{
			if (State == ClientState.BeforeOpen)
				throw new Exception("客户端未打开");
			if (State == ClientState.Opened)
				throw new Exception("客户端处于打开状态");
		}
		/// <summary>
		/// 获取或设置客户端使用的线程
		/// </summary>
		protected Thread Thread { get; set; }
		/// <summary>
		/// 获取或设置消息发送队列
		/// </summary>
		public Queue<Message> MessageQueue { get; protected set; }
		/// <summary>
		/// 用于线程同步的对象
		/// </summary>
		private object lockObject = new object();
		/// <summary>
		/// 是否应该停止工作
		/// </summary>
		private bool shouldStop = false;
		private ManualResetEvent manualEvent;
		private bool regularStop = false;
		
		/// <summary>
		/// 打开客户端
		/// </summary>
		public void Open()
		{
			ValidateBeforeOpen();
			if (!ConnectToServer())
			{
				string message = "与服务器建立连接失败";
				AddLog(message);
				throw new Exception(message);
			}
			State = ClientState.Opened;

			Thread = new Thread(new ThreadStart(() =>
			{
				while (true)
				{
					//发送尚未发送的消息
					while (MessageQueue.Count > 0)
					{
						Message message = MessageQueue.Dequeue();
						try
						{
							SendMessageToServer(message);
						}
						catch (Exception ex)
						{
							AddLog(ex.Message);
							lock (lockObject)
							{
								shouldStop = true;
							}
						}
					}

					//检查是否应该停止
					bool flag = false;
					bool lost = true;
					lock (lockObject)
					{
						if (shouldStop)
						{
							Socket.Close();
							Socket = null;
							Thread = null;
							flag = true;
							lost = !regularStop;
						}
					}
					if (flag)
					{
						State = ClientState.Closed;
						if (manualEvent != null)
							manualEvent.Set();
						if (lost)
							RaiseConnectionLostEvent();
						break;
					}

					//接收新消息
					if (Socket.Poll(100, SelectMode.SelectRead))		//查看是否有新消息
					{
						Message message = null;
						try
						{
							message = ReceiveMessageFromServer();
						}
						catch (Exception ex)
						{
							AddLog(ex.Message);
							lock (lockObject)
							{
								shouldStop = true;
								continue;
							}
						}
						ProcessMessage(message);
					}
				}
			}));
			Thread.Start();
		}

		/// <summary>
		/// 处理服务器发来的消息
		/// </summary>
		/// <param name="message">消息</param>
		/// <returns>如果消息合法并被成功处理，返回true；否则返回false</returns>
		protected bool ProcessMessage(Message message)
		{
			if (message == null)
			{
				lock (lockObject)
				{
					shouldStop = true;
				}
				return false;
			}
			else if (message is ErrorMessage)							//错误消息
			{
				var errorMessage = message as ErrorMessage;
				RaiseErrorEvent(errorMessage.Error);
				return true;
			}
			else if (message is LogInResultMessage)						//登录结果
			{
				var logInResultMessage = message as LogInResultMessage;
				if (logInResultMessage.IsLoggedIn)
				{
					User = logInResultMessage.User;
					UserState = UserStateKind.Online;
					AddLog("登录成功");
					RaiseUserLoggedInEvent(User);
					return true;
				}
				else
				{
					AddLog("登录失败：服务器拒绝登录");
					return true;
				}
			}
			else if (message is LogOutResultMessage)					//注销结果
			{
				if (User == null)
				{
					AddLog("未登录");
					return false;
				}
				var logOutResultMessage = message as LogOutResultMessage;
				User temp = User;
				User = null;
				UserState = UserStateKind.Offline;
				AddLog("注销成功");
				RaiseUserLoggedOutEvent(temp);
				return true;
			}
			else if (message is UserToUserMessage)						//从另一用户发来的消息
			{
				var userToUserMessage = message as UserToUserMessage;
				if (userToUserMessage.From != null && userToUserMessage.To == this.User)
				{
					if (userToUserMessage is ChatMessage)
					{
						var chatMessage = userToUserMessage as ChatMessage;
						AddLog(string.Format("收到了{0}向{1}发送的聊天内容", chatMessage.From, chatMessage.To));
						AddChatLog(new ChatLog(chatMessage.From, chatMessage.To, chatMessage.Content));
						return true;
					}
				}
				else
				{
					AddLog("非法数据");
				}
				return false;
			}
			else if (message is RefreshFriendsListMessage)				//刷新好友列表
			{
				var getFriendsListResultMessage = message as RefreshFriendsListMessage;
				Friends = new List<Sharpshooter.User>(
					from friend in getFriendsListResultMessage.Friends
					where friend != User
					select friend);
				RaiseFriendsListChangedEvent();
				return true;
			}
			else														//其他消息，不处理
			{
				AddLog("收到了未知消息：" + message.GetType().Name);
				return false;
			}
		}

		/// <summary>
		/// 登录
		/// </summary>
		/// <param name="username">用户名</param>
		/// <param name="password">密码</param>
		public void LogIn(string username, string password)
		{
			ValidateOpened();
			MessageQueue.Enqueue(new LogInMessage(username, password));
		}

		/// <summary>
		/// 注销
		/// </summary>
		public void LogOut()
		{
			ValidateOpened();
			MessageQueue.Enqueue(new LogOutMessage());
		}

		/// <summary>
		/// 和另一用户聊天
		/// </summary>
		/// <param name="user">另一用户</param>
		/// <param name="content">聊天内容</param>
		public void Chat(User user, byte[] content)
		{
			ValidateOpened();
			MessageQueue.Enqueue(new ChatMessage(this.User, user, content));
			AddLog(string.Format("向{0}发送聊天内容", user));
			AddChatLog(new ChatLog(this.User, user, content));
		}

		/// <summary>
		/// 连接到服务器
		/// </summary>
		/// <returns>是否成功</returns>
		protected bool ConnectToServer()
		{
			ValidateBeforeOpen();
			try
			{
				Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
				Socket.SendBufferSize = 0x1000000;
				Socket.ReceiveBufferSize = 0x1000000;
				Socket.Connect(ServerHost, ServerPort);
				State = ClientState.Opened;
				return true;
			}
			catch (SocketException ex)
			{
				AddLog("连接到服务器失败：" + ex.Message);
				return false;
			}
		}

		/// <summary>
		/// 关闭客户端，并回收资源
		/// </summary>
		public void Close()
		{
			Dispose();
		}

		/// <summary>
		/// 给服务器发送消息
		/// </summary>
		/// <param name="message">要发送的消息</param>
		protected void SendMessageToServer(Message message)
		{
			MessageHelper.Send(Socket, message);
			AddLog(string.Format("已向服务器发送{0}消息：{1}", message.GetType().Name, message));
		}

		/// <summary>
		/// 从服务器接收消息
		/// </summary>
		/// <returns>接收到的消息</returns>
		protected Message ReceiveMessageFromServer()
		{
			Message message = MessageHelper.Receive(Socket);
			if (message != null)
				AddLog(string.Format("已收到服务器发来的{0}消息：{1}", message.GetType().Name, message));
			return message;
		}

		/// <summary>
		/// 执行与释放或重置非托管资源相关的应用程序定义的任务。
		/// </summary>
		public void Dispose()
		{
			lock (lockObject)
			{
				if (shouldStop) return;
				regularStop = true;
				shouldStop = true;
				manualEvent = new ManualResetEvent(false);
			}
			manualEvent.WaitOne();
			ValidateClosed();
			Debug.Assert(Thread == null && Socket == null);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// 析构函数
		/// </summary>
		~Client()
		{
			Dispose();
		}

		/// <summary>
		/// 当添加了日志时发生。
		/// </summary>
		public event EventHandler<LogAddedEventArgs> LogAdded;
		/// <summary>
		/// 引发添加了日志事件。
		/// </summary>
		/// <param name="addedLog">添加的日志</param>
		protected void RaiseLogAddedEvent(Log addedLog)
		{
			if (LogAdded != null)
				LogAdded(this, new LogAddedEventArgs(addedLog));
		}
		/// <summary>
		/// 当添加聊天记录时发生。
		/// </summary>
		public event EventHandler<ChatLogAddedEventArgs> ChatLogAdded;
		/// <summary>
		/// 引发添加聊天记录事件。
		/// </summary>
		/// <param name="addedChatLog">聊天记录</param>
		protected void RaiseChatLogAddedEvent(ChatLog addedChatLog)
		{
			if (ChatLogAdded != null)
				ChatLogAdded(this, new ChatLogAddedEventArgs(addedChatLog));
		}
	}
}
